/** File name: generateSensorNet.cu **/

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <cuda.h>
#include "cellNetwork.h"
#include <Xlib.h>
#include "dataFrontier.cu"
#include "networkFrontier.cu"
#include "view.h"

#define WINDOW_TITLE "Generate Sensor Network from Frontier"
#define NUMBER_CYCLE 10
#define DATA_SENSOR_NET_NAME "dataSensorNet.cu"
#define NETWORK_SENSOR_NET_NAME "networkSensorNet.cu"
#define RANGE_SENSING 5
#define RANGE_COMMUNICATION 25
char DATA_FILE_NAME[] = "dataSensorNet.cu";

const int nbCells = sizeof(Cells)/sizeof(CellArray);
//If the number of cells between network and data are different, chosing the smaller one 
int dataSize = ((nbCells > NODE_NUMBER_FRONTIER) ? NODE_NUMBER_FRONTIER : nbCells);

int main(int argc, char **argv)
{
	/**********************Generating new_network*******************************/
	if (argc == 1)
	{
		printf("-- Generating sensor network data with default name: %s ", DATA_FILE_NAME);   
	}else if (argc == 2)
	{
		strcpy(DATA_FILE_NAME, argv[1]);
		printf("-- Generating sensor network data named: %s \n", DATA_FILE_NAME);
	}

	int plotSize, NSize, blockSize, nodeSize, channelSize, tabSize, bufferSize, nodeStateSize;
	int *now_h, *next_h, *now_d, *next_d;
	//bool *changed_h, *changed_d;
	canaux *channels_d;
	Colormap colormap;
	TabNeighbours *tabs_h, *tabs_d;
	Buffer *buffer_h, *buffer_d; 
	NodeState *nowState_h, *nowState_d, *nextState_h, *nextState_d, *buffState_d;

	plotSize = 1;
	NSize = 256;
	blockSize = 512;
	nodeSize = NODE_NUMBER_FRONTIER * sizeof(int);
	channelSize = NODE_NUMBER_FRONTIER * sizeof(canaux);	
	tabSize = sizeof(TabNeighbours) * NODE_NUMBER_FRONTIER;
	bufferSize = sizeof(Buffer) * NODE_NUMBER_FRONTIER;
	nodeStateSize = NODE_NUMBER_FRONTIER * sizeof(NodeState);

	Display *dsp = XOpenDisplay(NULL);	
	if (!dsp){ return 1;}
	colormap = DefaultColormap(dsp, 0);

	int screenNumber = DefaultScreen(dsp);
	unsigned long white = WhitePixel(dsp, screenNumber);
	unsigned long black = BlackPixel(dsp, screenNumber);	

	Window win = XCreateSimpleWindow(dsp,
				DefaultRootWindow(dsp),
				50, 50,// origin
				NSize*plotSize*3, NSize*plotSize*3,// size
				0, black,//border
				white//background
	);

	XStoreName(dsp, win, WINDOW_TITLE);
	XMapWindow(dsp, win);
	
	long eventMask = StructureNotifyMask;
	XSelectInput(dsp, win, eventMask);
	XEvent evt;
	do
	{
		XNextEvent(dsp, &evt);
	}while(evt.type != MapNotify);	

	GC gc = XCreateGC(dsp, win, 0, NULL);	
	
	/***********/
	//Allocating memory to host variables
	tabs_h = (TabNeighbours*) malloc(tabSize);
	buffer_h = (Buffer*) malloc(bufferSize);
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Allocating memory to device variable
	cudaMalloc((TabNeighbours**) &tabs_d, tabSize);
	cudaMalloc((Buffer**) &buffer_d, bufferSize);
	cudaMalloc((void**)&now_d, nodeSize);
	cudaMalloc((void**)&next_d, nodeSize);
	cudaMalloc((canaux**)&channels_d, channelSize);
	cudaMalloc((NodeState**)&nowState_d, nodeStateSize);
	cudaMalloc((NodeState**)&nextState_d, nodeStateSize);
	cudaMalloc((NodeState**)&buffState_d, nodeStateSize);
	
	//Copying data from host to device
	cudaMemcpy(now_d, now_h, nodeSize, cudaMemcpyHostToDevice);
	cudaMemcpy(channels_d, channels_h, channelSize, cudaMemcpyHostToDevice);

	/*****************Routing************************/
	//Initilizing state of the nodes
	printf("\nInitializing data ...\n");
	initNodeState(now_h, next_h, NODE_NUMBER_FRONTIER);
	showCells(Cells, dsp, colormap, win, gc, plotSize, now_h, NODE_NUMBER_FRONTIER);

	//Initializing
	Init<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(tabs_d, buffer_d, channels_d, NODE_NUMBER_FRONTIER);
	//Loop
	printf("\nCalculating route table ...\n");
	
	for (int i = 0; i < NODE_NUMBER_FRONTIER; i++)
	{
	    Distances<<<(NODE_NUMBER_FRONTIER / blockSize) + 1, blockSize>>>(tabs_d, buffer_d, channels_d, NODE_NUMBER_FRONTIER, i);
	    cudaThreadSynchronize();
	}

	//Copying values from device to host
	cudaMemcpy(tabs_h, tabs_d, tabSize, cudaMemcpyDeviceToHost);
	cudaMemcpy(buffer_h, buffer_d, bufferSize, cudaMemcpyDeviceToHost);
	
	//DumpTab(tabs_h, NODE_NUMBER_FRONTIER);
	//DumpMaxLoc(buffer_h, NODE_NUMBER);
	
	/************************Writing Dot file*********************************/

	//WriteNetwork("NetworkFed/network.dot", channels_h, NODE_NUMBER_FRONTIER);
	//WriteRouteTable("NetworkFed/routeTable.dot", tabs_h[6], channels_h);
	//WriteMaxRoutes("NetworkFed/maxroute.dot", tabs_h[0], channels_h);
	
	/*************************Routing Packet********************************/
	printf("\nClick on window to continue!\n");
	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);

	Packet *packets_h;
	int pac_num = 2;
	FileOfId *routes_h;	

	packets_h = (Packet*) malloc(sizeof(Packet) * pac_num);
	routes_h = (FileOfId*) malloc(sizeof(FileOfId) * pac_num);

	packets_h[0].srcId =0;
	packets_h[0].destId = 223;
	packets_h[1].srcId = 0;
	packets_h[1].destId = 225;
	for (int i = 0; i < pac_num; i++)
	{
	    RoutingPacket(packets_h[i], tabs_h, channels_h, routes_h[i]);
	}

	printf("\nWriting file %s (Wireless Sensor Network) - RANGE_SENSING = %d - RANGE_COMMUNICATION = %d ...\n", DATA_FILE_NAME, RANGE_SENSING, RANGE_COMMUNICATION);
	FILE *f;
	CellPosition position;
	CellImage image;
	f = fopen(DATA_FILE_NAME, "w");
	if (f == NULL)
	{
	    printf("\n*** Error open a file.\n");
	    exit(-1);
	}
	fprintf(f, "#include \"cellNetwork.h\"\n");
	fprintf(f, "int RANGE_SENSING = %d;\n", RANGE_SENSING);
	fprintf(f, "int RANGE_COMMUNICATION = %d;\n", RANGE_COMMUNICATION);

	fprintf(f, "\nCellArray NetCells[] = { ");

	int height, width;
	int radius_width, radius_height;
	for (int j = 0; j < pac_num; j++)
	{
	    for (int i = routes_h[j].limit - 1, turn = 0; i >= 0 ; i = i - 2, turn++)
	    {
	     	if (turn % RANGE_COMMUNICATION == 0)
	     	{
	     	    showCell(Cells[routes_h[j].newFound[i]], dsp, colormap, win, gc, plotSize, black);	 
		    position = Cells[routes_h[j].newFound[i]].position;
		    image = Cells[routes_h[j].newFound[i]].image;
		    height = image.extent.height;
		    width = image.extent.width;
		    //Displaying circle on window
		    radius_width = plotSize * width * RANGE_COMMUNICATION;
		    radius_height = plotSize * height * RANGE_COMMUNICATION;
	            XDrawArc(dsp, win, gc, (position.x * plotSize * width) - (radius_width / 2), (position.y * plotSize * height) - (radius_height / 2), radius_width, radius_height, 360*64, 360*64); 
		    fprintf(f, "{ {%d, %d} , ", position.x, position.y);
		    fprintf(f, "{ {%d, %d} , {", width, height);
		    for (int x = 0; x < width; x++)
		    {
			for (int y = 0; y < height; y++)
			{
			    if (x == (width - 1) && y == (height - 1))
			    {
			    	fprintf(f, "{%d, %d, %d} ", 0, 0, 0);
			    }else
			    {
			    	fprintf(f, "{%d, %d, %d}, ", 0, 0, 0);
			    }
			}
		    }
		    if ((i/2 >= RANGE_COMMUNICATION) || j < pac_num - 1 )
		    {
		    	fprintf(f, "} } },\n");
		    }else
		    {
			fprintf(f, "} } }\n");
		    }
	     	}
	    }
	}

	fprintf(f, "};");
	fclose(f);

	printf("\nClick on window to exit!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;
	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);

	//Releasing resources
	XDestroyWindow(dsp, win);
	XCloseDisplay(dsp);
		
	cudaFree(tabs_d);
	cudaFree(buffer_d);	
	cudaFree(now_d);
	cudaFree(next_d);
	cudaFree(channels_d);
	cudaFree(nowState_d);
	cudaFree(nextState_d);
	cudaFree(buffState_d);

	free(packets_h);
	free(routes_h);
	free(now_h);
	free(next_h);
	free(tabs_h);
	free(buffer_h);
	free(nowState_h);
	free(nextState_h);
	return 0;
}
